#include <QDir>
#include <QFile>
#include <QEventLoop>
#include <QDomDocument>
#include <QThread>
#include "biblereaderresourcemanager.h"
#include "biblereadercore.h"
#include <Logger.h>

BibleReaderResourceManager::BibleReaderResourceManager(QObject *parent) : QObject(parent)
{
    downloader = nullptr;
    listDownloader = nullptr;
    processResult = false;
}

BibleReaderResourceManager::~BibleReaderResourceManager()
{
    if (downloader) {
        delete downloader;
    }

    if (listDownloader) {
        delete listDownloader;
    }
}

QList<BRResource> BibleReaderResourceManager::getResources()
{
    return resources;
}

QUrl BibleReaderResourceManager::getResourceUrl() const
{
    return resourceUrl;
}

void BibleReaderResourceManager::setResourceUrl(const QUrl &value)
{
    resourceUrl = value;
}

bool BibleReaderResourceManager::removeRes(BRResource resource, BibleReaderCore *brCore)
{
    processResult = false;
    QString folderName = "";
    QDir tempDir;
    switch (resource.type) {
    case Bible:
        brCore->removeBibleVersion(resource.shortName);
        folderName.append(brCore->getConfigurator()->getBiblePathBase()).
                append(resource.shortName).append(QDir::separator());

        processResult = deleteDirectory(folderName);
        LOG_INFO() << folderName << processResult;
        break;

    case Dict:
        brCore->removeDictionary(resource.shortName);
        folderName.append(brCore->getConfigurator()->getDictPathBase()).
                append(resource.shortName).append(QDir::separator());

        processResult = deleteDirectory(folderName);
        LOG_INFO() << folderName << processResult;
        break;

    case Commentary:
        brCore->removeCommentary(resource.shortName);
        folderName.append(brCore->getConfigurator()->getBcPathBase()).
                append(resource.shortName).append(QDir::separator());
        processResult = deleteDirectory(folderName);
        LOG_INFO() << folderName << processResult;
        break;

    default:
        break;

    }
    return processResult;
}

bool BibleReaderResourceManager::installRes(BRResource resource, BibleReaderCore *brCore)
{
    processResult = false;
    QString baseUrl = "http://files.biblereader.cn/resources/";
    if (!downloader) {
        downloader = new BibleReaderDownloader(baseUrl, this);
        downloader->setErrorCode(QNetworkReply::NoError);
    }
    QString folderName = "";

    switch (resource.type) {
    case Bible:
        folderName.append(brCore->getConfigurator()->getBiblePathBase());

        downloader->setUrl(QUrl(baseUrl.append(resource.url)));
        downloader->setProperty("shortname", resource.shortName);
        downloader->setProperty("type", resource.type);
        downloader->setProperty("copypath", folderName);
        connect(downloader, &BibleReaderDownloader::downloadProgress, this, &BibleReaderResourceManager::showProgress);
        connect(downloader, &BibleReaderDownloader::finished, this, &BibleReaderResourceManager::copyRes);
        downloader->start();

        break;

    case Dict:
        folderName.append(brCore->getConfigurator()->getDictPathBase());

        downloader->setUrl(QUrl(baseUrl.append(resource.url)));
        downloader->setProperty("shortname", resource.shortName);
        downloader->setProperty("type", resource.type);
        downloader->setProperty("copypath", folderName);
        connect(downloader, &BibleReaderDownloader::downloadProgress, this, &BibleReaderResourceManager::showProgress);
        connect(downloader, &BibleReaderDownloader::finished, this, &BibleReaderResourceManager::copyRes);
        downloader->start();
        break;

    case Commentary:
        folderName.append(brCore->getConfigurator()->getBcPathBase());

        downloader->setUrl(QUrl(baseUrl.append(resource.url)));
        downloader->setProperty("shortname", resource.shortName);
        downloader->setProperty("type", resource.type);
        downloader->setProperty("copypath", folderName);
        connect(downloader, &BibleReaderDownloader::downloadProgress, this, &BibleReaderResourceManager::showProgress);
        connect(downloader, &BibleReaderDownloader::finished, this, &BibleReaderResourceManager::copyRes);
        downloader->start();
        break;
    default:
        break;

    }
    return processResult;
}

bool BibleReaderResourceManager::copyRes() {
    LOG_INFO() << "Got resource data!";
    processResult = false;
    BibleReaderDownloader *dl =  qobject_cast<BibleReaderDownloader*>(sender());
    LOG_INFO() << dl->getErrorCode();
    if (dl->getErrorCode() != QNetworkReply::NoError) {
        return false;
    }
    LOG_INFO() << "Starting to store data!";
    // create folder
    QDir tempDir;
    tempDir.setPath(dl->property("copypath").toString());
    tempDir.mkdir(dl->property("shortname").toString());
    tempDir.cd(dl->property("shortname").toString());
    // save file
    QString ext = "";
    ResourceType type = (ResourceType)dl->property("type").toInt();
    if (type == Bible) {
        ext = ".BIB";
    } else if (type == Dict) {
        ext = ".BIBDICT";
    } else if (type == Commentary) {
        ext = ".BIBCMT";
    }
    QString resFileName = dl->property("shortname").toString().append(ext);
    QString resFilePath = tempDir.filePath(resFileName);
    LOG_INFO() << resFilePath;
    QFile tmpFile;
    tmpFile.setFileName(resFilePath);
    if (tmpFile.open(QIODevice::WriteOnly)) {
        LOG_INFO() << tmpFile.write(downloader->getData());
        LOG_INFO() << tmpFile.fileName();
        tmpFile.flush();
        tmpFile.close();
        processResult = true;
        emit operationDone("copy", true);
        return true;
    } else {
        processResult = false;
        emit operationDone("copy", false);
        return false;
    }
}

void BibleReaderResourceManager::showProgress(qint64 a, qint64 b)
{
    LOG_INFO() << a << " ," << b;
    emit downloadProgress((double)a/(double)b * 100);
}

bool BibleReaderResourceManager::updateRes(BRResource resource, BibleReaderCore *brCore)
{
    processResult = false;
    QString baseUrl = "http://files.biblereader.cn/resources/";
    if (!downloader) {
        downloader = new BibleReaderDownloader(baseUrl, this);
        downloader->setErrorCode(QNetworkReply::NoError);
    }
    QString folderName = "";

    switch (resource.type) {
    case Bible:
        folderName.append(brCore->getConfigurator()->getBiblePathBase());

        downloader->setUrl(QUrl(baseUrl.append(resource.url)));
        downloader->setProperty("shortname", resource.shortName);
        downloader->setProperty("type", resource.type);
        downloader->setProperty("copypath", folderName);
        connect(downloader, &BibleReaderDownloader::downloadProgress, this, &BibleReaderResourceManager::showProgress);
        connect(downloader, &BibleReaderDownloader::finished, this, &BibleReaderResourceManager::copyRes);
        downloader->start();
        break;

    case Dict:
        folderName.append(brCore->getConfigurator()->getDictPathBase());

        downloader->setUrl(QUrl(baseUrl.append(resource.url)));
        downloader->setProperty("shortname", resource.shortName);
        downloader->setProperty("type", resource.type);
        downloader->setProperty("copypath", folderName);
        connect(downloader, &BibleReaderDownloader::downloadProgress, this, &BibleReaderResourceManager::showProgress);
        connect(downloader, &BibleReaderDownloader::finished, this, &BibleReaderResourceManager::copyRes);
        downloader->start();
        break;

    case Commentary:
        folderName.append(brCore->getConfigurator()->getBcPathBase());

        downloader->setUrl(QUrl(baseUrl.append(resource.url)));
        downloader->setProperty("shortname", resource.shortName);
        downloader->setProperty("type", resource.type);
        downloader->setProperty("copypath", folderName);
        connect(downloader, &BibleReaderDownloader::downloadProgress, this, &BibleReaderResourceManager::showProgress);
        connect(downloader, &BibleReaderDownloader::finished, this, &BibleReaderResourceManager::copyRes);
        downloader->start();
        break;
    default:
        break;

    }
    return processResult;
}

void BibleReaderResourceManager::refresh()
{
    listDownloader = new BibleReaderDownloader(resourceUrl, this);
    listDownloader->setErrorCode(QNetworkReply::NoError);
    connect(listDownloader, SIGNAL(finished()), this, SLOT(gotResources()));
    listDownloader->start();
}

void BibleReaderResourceManager::gotResources()
{
    QString replyString = listDownloader->getData();
    parseResources(replyString);

    emit refreshed();
}


bool BibleReaderResourceManager::deleteDirectory(const QString &path)
{
    if (path.isEmpty())
        return false;

    QDir dir(path);
    if(!dir.exists())
        return true;

    dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot);
    QFileInfoList fileList = dir.entryInfoList();
    foreach (QFileInfo fi, fileList)
    {
        LOG_INFO() << fi.absoluteFilePath();
        if (fi.isFile()) {
            if (!fi.dir().remove(fi.fileName())) {
                return false;
            }
        }else {
            if (!deleteDirectory(fi.absoluteFilePath())) {
                return false;
            }
        }
    }
    return dir.rmpath(dir.absolutePath());
}

QString BibleReaderResourceManager::type2str(ResourceType type)
{
    QString typeStr;
    switch (type) {
    case Book:
        typeStr = tr("Book");
        break;

    case Bible:
        typeStr = tr("Bible");
        break;

    case Commentary:
        typeStr = tr("Commentary");
        break;

    case Dict:
        typeStr = tr("Dict");
        break;

    case Map:
        typeStr = tr("Map");
        break;
    default:
        typeStr = tr("Unknown");
        break;

    }

    return typeStr;
}

void BibleReaderResourceManager::parseResources(QString resString)
{
    // delete all resource item
    resources.clear();
    LOG_INFO() << resString;

    if (!resString.isEmpty()) {
        QDomDocument doc;
        QString error;
        int row = 0, column = 0;

        if (doc.setContent(resString, &error, &row, &column)) {
            QDomElement root = doc.documentElement();

            // bibles
            QDomNode bibleRoot = root.firstChild();
            QDomNodeList bibles = bibleRoot.childNodes();

            for (int i = 0; i < bibles.count(); i++) {
                QDomElement bible = bibles.at(i).toElement();

                BRResource res;

                res.longName = bible.attribute("longname");
                res.shortName = bible.attribute("shortname");
                res.size = bible.attribute("size").toInt();
                res.version = bible.attribute("version").toInt();
                res.url = bible.attribute("url");
                res.type = Bible;
                res.typeStr = type2str(Bible);
                res.isinstalled = false;
                res.isupdated = false;

                resources.push_back(res);
            }

            // dicts
            QDomNode dictRoot = bibleRoot.nextSibling();
            QDomNodeList dicts = dictRoot.childNodes();

            for (int i = 0; i < dicts.count(); i++) {
                QDomElement dict = dicts.at(i).toElement();

                BRResource res;

                res.longName = dict.attribute("longname");
                res.shortName = dict.attribute("shortname");
                res.size = dict.attribute("size").toInt();
                res.version = dict.attribute("version").toInt();
                res.url = dict.attribute("url");
                res.type = Dict;
                res.typeStr = type2str(Dict);
                res.isinstalled = false;
                res.isupdated = false;

                resources.push_back(res);
            }

            // commentarys
            QDomNode cmtRoot = dictRoot.nextSibling();
            QDomNodeList cmts = cmtRoot.childNodes();

            for (int i = 0; i < cmts.count(); i++) {
                QDomElement cmt = cmts.at(i).toElement();

                BRResource res;

                res.longName = cmt.attribute("longname");
                res.shortName = cmt.attribute("shortname");
                res.size = cmt.attribute("size").toInt();
                res.version = cmt.attribute("version").toInt();
                res.url = cmt.attribute("url");
                res.type = Commentary;
                res.typeStr = type2str(Commentary);
                res.isinstalled = false;
                res.isupdated = false;

                resources.push_back(res);
            }
        } else {
            LOG_INFO() << resString;
        }
    }
}
